package net.wicp.tams.common.others;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.StringUtil;

public abstract class GitUtil {

	// 新建一个分支并同步到远程仓库
	public static Git newBranch(Git git, String branchName, String user, String pwd) throws GitAPIException {
		String newBranchIndex = "refs/heads/" + branchName;
		// 检查新建的分支是否已经存在，如果存在则将已存在的分支强制删除并新建一个分支
		List<Ref> refs = git.branchList().call();
		for (Ref ref : refs) {
			if (ref.getName().equals(newBranchIndex)) {
				System.out.println("Removing branch before");

				git.branchDelete().setBranchNames(branchName).setForce(true).call();
				break;
			}
		}
		// 新建分支
		Ref ref = git.branchCreate().setName(branchName).call();
		// 推送到远程
		CredentialsProvider cp = new UsernamePasswordCredentialsProvider(getUser(user), getPwd(pwd));
		git.push().add(ref).setCredentialsProvider(cp).call();
		return git;
	}

	public static Git newBranch(Git git, String branchName) throws GitAPIException {
		return newBranch(git, branchName, null, null);
	}

	/***
	 * 打开或是克隆一个git库
	 * 
	 * @param workDir
	 *            工作空间
	 * @param repo
	 *            gity库地址
	 * @param branch
	 *            分支
	 * @param user
	 *            用户名
	 * @param pwd
	 *            密码
	 * @return git
	 * @throws GitAPIException
	 *             exception
	 */
	public static Git openOrClone(String workDir, String repo, String branch, String user, String pwd)
			throws GitAPIException {
		try {
			Git git = Git.open(new File(workDir));
			return git;
		} catch (IOException e) {
			CredentialsProvider cp = new UsernamePasswordCredentialsProvider(getUser(user), getPwd(pwd));
			File dir = new File(workDir);
			Git git = Git.cloneRepository().setDirectory(dir).setCloneSubmodules(true).setURI(repo).setBranch(branch)
					.setCredentialsProvider(cp).call();
			return git;
		}
	}

	public static PullResult pull(Git git, String user, String pwd) throws Exception {
		CredentialsProvider cp = new UsernamePasswordCredentialsProvider(getUser(user), getPwd(pwd));
		return git.pull().setCredentialsProvider(cp).call();
	}

	public static Git openOrClone(String workDir, String repo, String branch) throws GitAPIException {
		return openOrClone(workDir, repo, branch, null, null);
	}

	public static String getUser(String user) {
		if (StringUtil.isNotNull(user) && !"none".equals(user)) {
			return user;
		} else {
			return Conf.get("common.others.git.username");
		}
	}

	public static String getPwd(String pwd) {
		if (StringUtil.isNotNull(pwd) && !"none".equals(pwd)) {
			return pwd;
		} else {
			return Conf.get("common.others.git.password");
		}
	}

	/**
	 * 由git库地址得到主机地址，出错或者格式不合法返回null
	 * 
	 * @param repos
	 *            git库地址
	 * @return url
	 */
	public static String getHostUrl(String repos) {
		if (StringUtils.isBlank(repos)) {
			return null;
		}
		URL url = null;
		try {
			url = new URL(repos);
			return String.format("%s://%s:%d", url.getProtocol(), url.getHost(), url.getPort());
		} catch (MalformedURLException e) {
			return null;
		}
	}

	public static Git merge(Git git, String branchSrc, String branchDst, boolean forceCreateDst)
			throws GitAPIException {
		List<Ref> localBranchRefs = git.branchList().call();
		List<Ref> remoteBranchRefs = git.branchList().setListMode(ListBranchCommand.ListMode.REMOTE).call();
		Ref srcLocalRef = getBranchRef(localBranchRefs, branchSrc);
		Ref srcRemoteRef = getBranchRef(remoteBranchRefs, branchSrc);
		if (null == srcLocalRef && null == srcRemoteRef) {
			return git;
		} else if (null == srcLocalRef && null != srcRemoteRef) {
			git.branchCreate().setName(branchSrc).setStartPoint(srcRemoteRef.getName()).call();
		}
		// git local branchsrc not null

		Ref dstLocalRef = getBranchRef(localBranchRefs, branchDst);
		Ref dstRemoteRef = getBranchRef(remoteBranchRefs, branchDst);
		if (null == dstLocalRef && null == dstRemoteRef) {
			if (forceCreateDst) {
				// git.checkout().setName(branchSrc).call();
				git.branchCreate().setName(branchDst).setStartPoint(branchSrc).call();
			} else {
				throw new RuntimeException(String.format("branch[%s] is not exist", branchDst));
			}
		}
		if (null == dstLocalRef && null != dstRemoteRef) {
			git.branchCreate().setName(branchDst).setStartPoint(dstRemoteRef.getName()).call();
		}

		localBranchRefs = git.branchList().call();
		srcLocalRef = getBranchRef(localBranchRefs, branchSrc);
		// assert srcLocalRef != null

		git.checkout().setName(branchDst).call();
		MergeResult mergeResult = git.merge().include(srcLocalRef).call();
		checkMergeResult(mergeResult);

		return git;
	}

	public static Git merge4Master(Git git, String branch) throws GitAPIException {
		return merge(git, "master", branch, false);
	}

	public static Git push2Branch(Git git, String branch, String user, String pwd) throws GitAPIException {
		merge4Master(git, branch);
		git.push().add(branch)
				.setCredentialsProvider(new UsernamePasswordCredentialsProvider(getUser(user), getPwd(pwd))).call();
		return git;
	}

	public static Git add(Git git, String filePath) throws GitAPIException {
		git.add().setUpdate(false).addFilepattern(filePath).call();
		return git;
	}
	
	public static Git add(Git git) throws GitAPIException {
		return  add(git,".");
	}

	public static Git commit(Git git, String message) throws GitAPIException {
		add(git);
		git.commit().setMessage(message).call();
		return git;
	}
	
	public static Git commitNoAdd(Git git, String message) throws GitAPIException {
		git.commit().setMessage(message).call();
		return git;
	}
	

	public static Git push(Git git, String branch, String user, String pwd) throws GitAPIException {
		git.push().add(branch)
				.setCredentialsProvider(new UsernamePasswordCredentialsProvider(getUser(user), getPwd(pwd))).call();
		return git;
	}

	public static Git push2Branch(Git git, String branch) throws GitAPIException {
		return push2Branch(git, branch, null, null);
	}

	public static Git push2Master(Git git, String branch, String user, String pwd) throws GitAPIException {
		merge(git, branch, "master", true);
		git.push().add("master")
				.setCredentialsProvider(new UsernamePasswordCredentialsProvider(getUser(user), getPwd(pwd))).call();

		return git;
	}

	public static Git push2Master(Git git, String branch) throws GitAPIException {
		return push2Master(git, branch, null, null);
	}

	private static void checkMergeResult(MergeResult mergeResult) {
		if (null == mergeResult) {
			throw new RuntimeException("merge result is empty");
		} else if (!mergeResult.getMergeStatus().isSuccessful()) {
			if (null != mergeResult.getConflicts()) {
				throw new RuntimeException(
						"files with any conflict: " + mergeResult.getConflicts().keySet().toString());
			} else {
				throw new RuntimeException(mergeResult.toString());
			}
		}
	}

	private static Ref getBranchRef(List<Ref> branchRefs, String branchName) {
		for (Ref eachRef : branchRefs) {
			if (eachRef.getName().equals("refs/heads/" + branchName)
					|| eachRef.getName().equals("refs/remotes/origin/" + branchName)) {
				return eachRef;
			}
		}
		return null;
	}
}
